#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <glog/logging.h>

#include "reader.h"
#include "writer.h"
#include "value.h"
#include "ji_utils.h"
#include "SampleAlgorithm.hpp"

#define JSON_ALERT_FLAG_KEY ("is_alert")
#define JSON_ALERT_FLAG_TRUE true
#define JSON_ALERT_FLAG_FALSE false


SampleAlgorithm::SampleAlgorithm()
{    
}

SampleAlgorithm::~SampleAlgorithm()
{    
    UnInit();
}

JiErrorCode SampleAlgorithm::Init()
{
    // 从默认的配置文件读取相关配置参数
    const char *configFile = "/usr/local/ev_sdk/config/algo_config.json";
    SDKLOG(INFO) << "Parsing configuration file: " << configFile;
    std::ifstream confIfs(configFile);
    if (confIfs.is_open())
    {
        size_t len = GetFileLen(confIfs);
        char *confStr = new char[len + 1];
        confIfs.read(confStr, len);
        confStr[len] = '\0';
        SDKLOG(INFO) << "Configs:"<<confStr;
        mConfig.ParseAndUpdateArgs(confStr);
        delete[] confStr;
        confIfs.close();
    }
    mDetector = std::make_shared<SampleDetector>();
    std::string model_path = "/usr/local/ev_sdk/model/yolov5s_1684x_f16.bmodel";
    std::string class_file_path = "/usr/local/ev_sdk/coco.names";
    mDetector->Init(model_path, class_file_path, mConfig.algo_config.thresh);
    return JISDK_RET_SUCCEED;
}


JiErrorCode SampleAlgorithm::UnInit()
{
    if(mDetector.get() != nullptr)
    {
        SDKLOG(INFO) << "uninit";
        mDetector->UnInit();
        mDetector.reset();
    }
    return JISDK_RET_SUCCEED;
}

JiErrorCode SampleAlgorithm::UpdateConfig(const char *args)
{
    if (args == nullptr)
    {
        SDKLOG(ERROR) << "mConfig string is null ";
        return JISDK_RET_FAILED;
    }
    mConfig.ParseAndUpdateArgs(args);
    return JISDK_RET_SUCCEED;
}

JiErrorCode SampleAlgorithm::GetOutFrame(JiImageInfo **out, unsigned int &outCount)
{
    outCount = mOutCount;

    mOutImage[0].nWidth = mOutputFrame.cols;
    mOutImage[0].nHeight = mOutputFrame.rows;
    mOutImage[0].nFormat = JI_IMAGE_TYPE_BGR;
    mOutImage[0].nDataType = JI_UNSIGNED_CHAR;
    mOutImage[0].nWidthStride = mOutputFrame.step;
    mOutImage[0].pData = mOutputFrame.data;

    *out = mOutImage;
    return JISDK_RET_SUCCEED;
}

JiErrorCode SampleAlgorithm::Process(const bm_image &inFrame, const char *args, JiEvent &event)
{   
    REC_TIME(t0);
    SDKLOG(INFO)<<"inFrame:"<<inFrame.width<<","<<inFrame.height;
    //由于roi配置是归一化的坐标,所以输出图片的大小改变时,需要更新ROI的配置  
    if (inFrame.width != mConfig.current_in_frame_size.width || inFrame.height != mConfig.current_in_frame_size.height)
    {
        SDKLOG(INFO)<<"Update ROI Info...";
        mConfig.UpdateROIInfo(inFrame.width, inFrame.height);
    }

    //如果输入的参数不为空且与上一次的参数不完全一致,需要调用更新配置的接口
    if(args != nullptr && mStrLastArg != args) 
    {   
        mStrLastArg = args;
        SDKLOG(INFO) << "Update args:" << args;
        mConfig.ParseAndUpdateArgs(args);
    }

    // 针对整张图进行推理,获取所有的检测目标,并过滤出在ROI内的目标
    std::vector<std::vector<BoxInfo>> detectedObjects;
    bool det_flag = false;   
    std::vector<BoxInfo> validTargets;
    // 算法处理
    std::vector<bm_image> input_images;
    input_images.push_back(inFrame);
    

    mDetector->ProcessImage(input_images, detectedObjects);    

    REC_TIME(t1);
    SDKLOG(INFO) << "detect processing time(ms):" << RUN_TIME(t1-t0);
    if(detectedObjects.size())
    {        
        det_flag = true;
        //过滤出行人
        /*for(auto iter = detectedObjects[0].begin(); iter != detectedObjects[0].end();)
        {
            if(iter->label ==0 )
            {
                iter++;
            }
            else
            {
                iter = detectedObjects[0].erase(iter);
            }
        }   
    
        */
        for (auto &obj : detectedObjects[0])
        {
            for (auto &roi : mConfig.current_roi_orig_polygons)
            {
                int mid_x = (obj.x1 + obj.x2) / 2;
                int mid_y = (obj.y1 + obj.y2) / 2;
                // 当检测的目标的中心点在ROI内的话，就视为闯入ROI的有效目标
                if (WKTParser::InPolygon(roi, cv::Point(mid_x, mid_y)))
                {
                        validTargets.emplace_back(obj);
                }           
            }
        }
    }
    SDKLOG_FIRST_N(INFO, 5) << " valid targets :  " << validTargets.size();
    // 此处示例业务逻辑：当算法检测到有行人闯入时，就报警
    bool isNeedAlert = false; // 是否需要报警
    // 创建输出图
    //bm_image bm_tmp = const_cast<bm_image&>(inFrame);
    cv::bmcv::toMAT(const_cast<bm_image*>(&inFrame), mOutputFrame);
    SDKLOG(INFO)<<"output frame size:"<<mOutputFrame.cols<<","<<mOutputFrame.rows;
    // 画ROI区域
    if (mConfig.draw_roi_area && mConfig.draw_result && !mConfig.current_roi_orig_polygons.empty())
    {
            DrawPolygon(mOutputFrame,
                    mConfig.current_roi_orig_polygons,
                    cv::Scalar(mConfig.roi_color[0], mConfig.roi_color[1], mConfig.roi_color[2]),
                    mConfig.roi_color[3],
                    cv::LINE_AA,
                    mConfig.roi_line_thickness,
                    mConfig.roi_fill);
    }

    // 判断是否要要报警
    if (validTargets.size() > 0)
    {        
        isNeedAlert = true;
    }
    //并将检测到的在ROI内部的目标画到图上
    if(mConfig.draw_result)
    {
            for (auto & obj : validTargets)
            {
                std::stringstream ss;
                ss << obj.name;
                if (mConfig.draw_confidence)
                {
                    ss.precision(0);
                    ss << std::fixed << obj.score * 100 << "%";
                }
                cv::Rect rect = cv::Rect{int(obj.x1), int(obj.y1), int(obj.x2 - obj.x1), int(obj.y2 - obj.y1)};
                DrawRectText(mOutputFrame,
                                 rect, ss.str(),
                                 mConfig.target_rect_line_thickness,
                                 cv::LINE_AA,
                                 cv::Scalar(mConfig.target_rect_color[0], mConfig.target_rect_color[1], mConfig.target_rect_color[2]),
                                 mConfig.target_rect_color[3],
                                 mConfig.target_text_height,
                                 cv::Scalar(mConfig.text_foreground_color[0], mConfig.text_foreground_color[1], mConfig.text_foreground_color[2]),
                                 cv::Scalar(mConfig.text_background_color[0], mConfig.text_background_color[1], mConfig.text_background_color[2])
                                 ); 
            
            }
    }
    if (isNeedAlert && mConfig.draw_result && mConfig.draw_warning_text)
    {
        DrawText(mOutputFrame,
                 mConfig.warning_text_map[mConfig.language],
                 mConfig.warning_text_size,
                 cv::Scalar(mConfig.warning_text_foreground_color[0], mConfig.warning_text_foreground_color[1], mConfig.warning_text_foreground_color[2]),
                 cv::Scalar(mConfig.warning_text_background_color[0], mConfig.warning_text_background_color[1], mConfig.warning_text_background_color[2]),
                 mConfig.warning_text_left_top
                 );
    }

    // 将结果封装成json字符串
    bool jsonAlertCode = JSON_ALERT_FLAG_FALSE;
    if (isNeedAlert)
    {
        jsonAlertCode = JSON_ALERT_FLAG_TRUE;
    }
    Json::Value jRoot;
    Json::Value jAlgoValue;
    Json::Value jDetectValue;
    
    jAlgoValue[JSON_ALERT_FLAG_KEY] = jsonAlertCode;    
    jAlgoValue["target_info"].resize(0);
    for (auto &obj : validTargets)
    {        
        Json::Value jTmpValue;
        jTmpValue["x"] = int(obj.x1);
        jTmpValue["y"] = int(obj.y1);
        jTmpValue["width"] = int(obj.x2 - obj.x1);
        jTmpValue["height"] = int(obj.y2 - obj.y1);
        jTmpValue["name"] = obj.name;
        jTmpValue["confidence"] = obj.score;
        jAlgoValue["target_info"].append(jTmpValue);
    }
    jRoot["algorithm_data"] = jAlgoValue;
    
    //create model data
    jDetectValue["objects"].resize(0);
    if(det_flag)
    {   
            for (auto &obj : detectedObjects[0])
            {
                Json::Value jTmpValue;
                jTmpValue["x"] = int(obj.x1);
                jTmpValue["y"] = int(obj.y1);
                jTmpValue["width"] = int(obj.x2 - obj.x1);
                jTmpValue["height"] = int(obj.y2 - obj.y1);
                jTmpValue["name"] = obj.name;
                jTmpValue["confidence"] = obj.score;
                jDetectValue["objects"].append(jTmpValue);
            }
    }
    jRoot["model_data"] = jDetectValue;

    Json::StreamWriterBuilder writerBuilder;
    writerBuilder.settings_["precision"] = 2;
    writerBuilder.settings_["emitUTF8"] = true;    
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    std::ostringstream os;
    jsonWriter->write(jRoot, &os);
    mStrOutJson = os.str();    
    // 注意：JiEvent.code需要根据需要填充，切勿弄反
    if (isNeedAlert)
    {
        event.code = JISDK_CODE_ALARM;
    }
    else
    {
        event.code = JISDK_CODE_NORMAL;
    }
    event.json = mStrOutJson.c_str();    
    REC_TIME(t2);
    SDKLOG(INFO) << "output processing time(ms):" << RUN_TIME(t2-t1);
    return JISDK_RET_SUCCEED;
}

